package cn.b.sky.mes.pp.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import javax.annotation.Resource;

import cn.b.sky.mes.pp.dao.ProductOrderDao;
import cn.b.sky.mes.pp.dao.ProductionControlDao;
import cn.b.sky.mes.pp.dao.ProductionOrderAttachmentDao;
import cn.b.sky.mes.pp.dao.ProductionOrderDao;
import cn.b.sky.mes.pp.enums.OrderStatusCode;
import cn.b.sky.mes.pp.enums.PorderStatusCode;
import cn.b.sky.mes.pp.enums.ProductionControlStatusCode;
import cn.b.sky.mes.pp.model.condition.ProductionOrderCondition;
import cn.b.sky.shop.activiti.service.impl.EvunProcessEngine;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import cn.b.sky.mes.cm.model.Material;
import cn.b.sky.mes.cm.model.ProcessOperation;
import cn.b.sky.mes.cm.service.MaterialService;
import cn.b.sky.mes.cm.service.ProcessService;
import cn.b.sky.common.constants.SystemConstant;
import cn.b.sky.common.exception.ImeException;
import cn.b.sky.common.model.Page;
import cn.b.sky.common.utils.FTPUtils;
import cn.b.sky.common.utils.SkyFileUtils;
import cn.b.sky.common.utils.SpringBeanUtil;
import cn.b.sky.user.SecurityContextUtil;
import cn.b.sky.mes.cm.enums.ECodeEnums;
import cn.b.sky.mes.cm.enums.NumberRuleEnums;
import cn.b.sky.user.model.Attachment;
import cn.b.sky.user.service.AttachmentService;
import cn.b.sky.user.service.ECodeService;
import cn.b.sky.user.service.NumberRuleService;
import cn.b.sky.user.service.model.ECode;
import cn.b.sky.mes.pp.model.OperationArchives;
import cn.b.sky.mes.pp.model.ProductOrder;
import cn.b.sky.mes.pp.model.ProductionControl;
import cn.b.sky.mes.pp.model.ProductionOrder;
import cn.b.sky.mes.pp.model.ProductionOrderAttachment;
import cn.b.sky.mes.pp.service.ProductionControlService;
import cn.b.sky.mes.pp.service.ProductionOrderService;
import cn.b.sky.mes.pp.service.RequirementStrategyService;

import com.alibaba.fastjson.JSONObject;

/**
 * <strong>Title : ProductionOrderServiceImpl.java<br>
 * </strong> <strong>Package : cn.b.sky.mes.pp.service.impl<br>
 * </strong> <strong>Description : </strong>生产订单业务实现<br>
 * <strong>Create on : 2015年10月6日 上午11:01:48<br>
 * </strong>
 * <p>
 * 
 * @author dapeng.li<br>
 * @version <strong>v1.0.0</strong><br>
 * <br>
 *          <strong>修改历史:</strong><br>
 *          修改人 | 修改日期 | 修改描述<br>
 *          -------------------------------------------<br>
 * <br>
 * <br>
 */

@Service("orderService")
public class ProductionOrderServiceImpl implements ProductionOrderService {

  private static final Logger LOGGER = LoggerFactory.getLogger(ProductionOrderServiceImpl.class);

  @Autowired
  private ProductionOrderDao productionOrderDao;

  @Autowired
  private ProductionControlDao productionControlDao;

  @Autowired
  private ProductOrderDao productOrderDao;

  @Autowired
  private ProductionOrderAttachmentDao productionOrderAttachmentDao;

  @Resource
  private ProductionControlService productionControlService;

  @Resource
  NumberRuleService numberRuleService;

  @Resource
  ECodeService eCodeService;

  @Resource
  MaterialService materialService;

  @Resource
  private EvunProcessEngine skyProcessEngine;

  @Resource
  ProcessService processService;

  @Resource
  AttachmentService attachmentService;

  @Resource
  RequirementStrategyService requirementStrategyService;

  @Override
  public Page getProductionOrdersByCondition(ProductionOrderCondition condition) {

    if (condition == null) {
      condition = new ProductionOrderCondition();
    }
    // 默认按照创建时间排序
    if (StringUtils.isBlank(condition.getOrderField())) {
      condition.setOrderField("CreateDateTime");
    }
    List<ProductionOrder> order = productionOrderDao.selectProductionOrdersByCondition(condition);
    int count = productionOrderDao.selectProductionOrderCount(condition);
    condition.setData(order);
    condition.setTotalCount(count);
    return condition;
  }

  @Override
  public Page getUncompletedOrdersByCondition(ProductionOrderCondition condition) {
    if (condition == null) {
      condition = new ProductionOrderCondition();
    }
    condition.setStatuseCode(OrderStatusCode.COMPLETE.getValue());
    int count = productionOrderDao.getUncompletedProductionOrdersCount(condition);
    if(count > 0) {
      List<ProductionOrder> order = productionOrderDao.selectUncompletedProductionOrders(condition);
      condition.setData(order);
      condition.setTotalCount(count);
    }
    return condition;
  }

  @Override
  public ProductionOrder getProductionOrderByPk(String siteCode, String productionOrderNum) {
    if (StringUtils.isBlank(siteCode) || StringUtils.isBlank(productionOrderNum)) {
      return null;
    }
    return this.productionOrderDao.selectProductionOrdersByPk(siteCode, productionOrderNum);
  }

  @Override
  public void deleteProductionOrderByPk(String siteCode, String productionOrderNum) {
    if (!StringUtils.isBlank(siteCode) && !StringUtils.isBlank(productionOrderNum)) {
      productionOrderDao.deleteProductionOrdeByPK(siteCode, productionOrderNum);
    }
  }

  @Override
  public void saveProductionOrder(ProductionOrder order) {

    if (order != null && !StringUtils.isBlank(order.getSiteCode())) {
      if (StringUtils.isBlank(order.getProductionOrderNum())) {
        order.setProductionOrderNum(numberRuleService.selectNextNumber(order.getSiteCode(),
            NumberRuleEnums.PRODUCTIONORDERENCODINGRULE.getValue()));
      }
      productionOrderDao.insertProductionOrder(order);
      this.batchSaveAttachment(order.getAttachment(), order.getProductionOrderNum(), order.getSiteCode());
    }
  }

  private void batchSaveAttachment(List<ProductionOrderAttachment> attachments, String productionOrderNum,
                                   String siteCode) {
    if (attachments != null && attachments.size() > 0 && productionOrderNum != null) {
      for (ProductionOrderAttachment attachment : attachments) {
        if (attachment != null && !StringUtils.isBlank(attachment.getAttachmentCode())) {
          attachment.setProductionOrderNum(productionOrderNum);
          attachment.setSiteCode(siteCode);
          productionOrderAttachmentDao.insertProductionOrderAttachment(attachment);
        }
      }
    }

  }

  @Override
  public void modifyProductionOrder(ProductionOrder order) {
    if (order != null && !StringUtils.isBlank(order.getProductionOrderNum())) {
      productionOrderDao.updateProductionOrder(order);
      productionOrderAttachmentDao.deleteAttachmentByProductionNum(order.getSiteCode(), order.getProductionOrderNum());
      batchSaveAttachment(order.getAttachment(), order.getProductionOrderNum(), order.getSiteCode());
    }
  }

  @Override
  public void modifyNoCreateProductionOrder(ProductionOrder order) {

    if (order != null && !StringUtils.isBlank(order.getProductionOrderNum())) {
      productionOrderDao.updateNoCreateProductionOrder(order);
    }
  }

  @Override
  public List<ProductionControl> getProductionControlsByOrder(String siteCode, String productionOrderNum) {
    if (StringUtils.isBlank(siteCode) || StringUtils.isBlank(productionOrderNum)) {
      return null;
    }
    return productionControlDao.selectProductionControlByOrder(siteCode, productionOrderNum);
  }

  @Override
  public void releaseProductionControl(String orderJson) {
    if (StringUtils.isBlank(orderJson)) {
      return;
    }
    ProductionOrder order = JSONObject.parseObject(orderJson, ProductionOrder.class);
    if (order == null || StringUtils.isBlank(order.getSiteCode())
        || StringUtils.isBlank(order.getProductionOrderNum())) {
      return;
    }
    Material material = materialService.selectMaterial(order.getSiteCode(), order.getMaterialCode());
    if (material == null) {
      return;
    }
    List<ProductionControl> controls = order.getControls();
    if (controls == null) {
      return;
    }
    for (ProductionControl control : controls) {
      //非空判断
      if (control == null || null == control.getPlannedQuantity() || control.getPlannedQuantity().doubleValue() <= 0) {
        continue;
      }
      //未投产
      control.setStatuseCode(ProductionControlStatusCode.CREATE.getValue());
      control.setSiteCode(order.getSiteCode());
      setControlInfo(order, control);
      //生产批量,按批量分解
      psizeSplitProduction(material.getMakelotsize(), control);

    }
    // 物料拉动需求
    if (requirementStrategyService.isPullStrategyEnable()) {
      requirementStrategyService.createProductionOrderRequirements(order, controls);
    }
  }

  @Override
  public void modifyProductionOrderToIssued(String orderJson) {
    ProductionOrder orderProcess = JSONObject.parseObject(orderJson, ProductionOrder.class);
    if (orderProcess == null || StringUtils.isBlank(orderProcess.getProductionOrderNum())) {
      return;
    }
    ProductionOrder order = new ProductionOrder();
    order.setStatuseCode(OrderStatusCode.PLACE_ORDER.getValue());
    //分解数量修改
    order.setReleasedQuantity(orderProcess.getReleasedQuantity());
    order.setProductionOrderNum(orderProcess.getProductionOrderNum());
    order.setSiteCode(orderProcess.getSiteCode());
    this.modifyNoCreateProductionOrder(order);
  }

  /**
   * 生产批量,按批量分解
   */
  private void psizeSplitProduction(Long size, ProductionControl control) {
    double quantity = control.getPlannedQuantity().doubleValue();
    if (size != null && size.longValue() > 0) {
      for (int i = 1; i <= quantity / size.doubleValue(); i++) {
        control.setProductionControlNum(
            numberRuleService.selectNextNumber(control.getSiteCode(), NumberRuleEnums.PRODUCTIONCONTROLNO.getValue()));
        control.setPlannedQuantity(size.doubleValue());
        productionControlDao.insertProductionControl(control);
      }
      if (quantity % size.doubleValue() > 0) {
        control.setProductionControlNum(
            numberRuleService.selectNextNumber(control.getSiteCode(), NumberRuleEnums.PRODUCTIONCONTROLNO.getValue()));
        control.setPlannedQuantity(quantity % size.doubleValue());
        productionControlDao.insertProductionControl(control);
      }
    } else {
      control.setProductionControlNum(
          numberRuleService.selectNextNumber(control.getSiteCode(), NumberRuleEnums.PRODUCTIONCONTROLNO.getValue()));
      productionControlDao.insertProductionControl(control);
    }
  }

  /**
   * 设置其他信息
   */
  private void setControlInfo(ProductionOrder order, ProductionControl control) {
    control.setProductionOrderNum(order.getProductionOrderNum());
    control.setSiteCode(order.getSiteCode());
    control.setProcessCode(order.getProcessCode());
    control.setProcessRev(order.getProcessRev()); // 版本
    control.setMaterialCode(order.getMaterialCode());
    control.setOrderTypeCode(order.getOrderTypeCode());
    control.setProductionlotNum(order.getProductionlotNum());
    control.setPlannedstartDateTime(order.getPlannedstartDateTime());
    control.setPlannedendDateTime(order.getPlannedendDateTime());
    control.setRequirementDate(order.getRequirementDate());
    control.setCreateUser(order.getCreateUser());
    control.setCreateDateTime(new Date());
    control.setModifyUser(order.getModifyUser());
    control.setModifyDateTime(new Date());
    control.setCustomer(order.getCustomer());
    control.setCustomerOrder(order.getCustomerOrder());
    control.setPriority(order.getPriority());
  }

  @Override
  public void doModifyProductionOrder(ProductionOrder order) {
    //保存数据之前校验数据状态,考虑到事物的一致性，防止多人操作出现错误，修改的时候如果其他用户已经下达，则提示不能修改，所校验要和修改在同一事物里
    ProductionOrder oldOrder = this.getProductionOrderByPk(order.getSiteCode(), order.getProductionOrderNum());
    if (null != oldOrder && !OrderStatusCode.CREATE.getValue().equals(oldOrder.getStatuseCode())) {
      ECode ecode = eCodeService.selectEcodeByECodeTypeAndLanguageCodeAndECode(oldOrder.getStatuseCode(),
          ECodeEnums.PRODUCTIONORDER_TYPE.getCode(), order.getLanguageCode());
      throw ImeException.newException("productionOrder_notEdit.M", new String[] { ecode.geteCodeText() });
    }
    this.modifyProductionOrder(order);
  }

  @Override
  public void modifyIssuedProductionOrder(String productionOrderNum, String siteCode) {
    if (StringUtils.isBlank(siteCode) || StringUtils.isBlank(productionOrderNum)) {
      return;
    }
    //删除sfc订单
    productionControlDao.deleteProductionControlByOrder(siteCode, productionOrderNum);
    ProductionOrder order = new ProductionOrder();
    order.setStatuseCode(OrderStatusCode.CREATE.getValue());
    order.setProductionOrderNum(productionOrderNum);
    order.setSiteCode(siteCode);
    order.setModifyDateTime(new Date());
    order.setModifyUser(SecurityContextUtil.getCurrentUserName());
    //清空下达数量
    order.setReleasedQuantity(0d);
    this.modifyNoCreateProductionOrder(order);
  }

  @Override
  public void doBackProductionOrder(String productionOrderNum, String siteCode) {

    ProductionOrder order = this.getProductionOrderByPk(siteCode, productionOrderNum);
    if (order != null && !OrderStatusCode.PLACE_ORDER.getValue().equals(order.getStatuseCode())) {
      ECode ecode = eCodeService.selectEcodeByECodeTypeAndLanguageCodeAndECode(order.getStatuseCode(),
          ECodeEnums.PRODUCTIONORDER_TYPE.getCode(), SecurityContextUtil.getLanguageCode());
      throw ImeException.newException("ismaking_notbackIssued.M", new String[] { ecode.geteCodeText() });
    }
    order.setModifyDate(new Date());
    order.setModifyUser(SecurityContextUtil.getCurrentUserName());
    //撤回时校验是否已经有状态是不是未投产的sfc订单
    List<ProductionControl> controls = productionControlDao.selectRuningProductionControl(siteCode, productionOrderNum,
        ProductionControlStatusCode.CREATE.getValue());
    if (controls != null && controls.size() > 0) {
      throw ImeException.newException("productionOrder_notbackIssued.M");
    }

    modifyIssuedProductionOrder(order.getProductionOrderNum(), siteCode);

  }

  @Override
  public boolean placeOrder(String[] orderId) {
    return false;
  }

  /**
   * <strong>Title : getProductionOrdersStatus<br>
   * </strong> <strong>Description : </strong>生产订单状态分析<br>
   * <strong>Create on : 2015年10月14日 下午5:21:32<br>
   * </strong>
   * <p>
   * 
   * @param condition
   * @param ecode
   * @return
   * @see ProductionOrderService#getProductionOrdersStatus(ProductionOrderCondition,
   *      java.util.List)
   * @throws
   * @author dapeng.li<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  @Override
  public Map<String, Integer> getProductionOrdersStatus(ProductionOrderCondition condition, List<ECode> ecode) {
    if (condition == null) {
      condition = new ProductionOrderCondition();
    }
    HashMap<String, Integer> orderStatusMap = new HashMap<String, Integer>();
    int totalCount = 0;
    List<ECode> ecodes = eCodeService.selectECodeList(ECodeEnums.PRODUCTIONORDER_TYPE.getCode(),
        SecurityContextUtil.getLanguageCode());
    String[] statuseCodes = new String[ecodes.size()];

    for (int i = 0; i < ecodes.size(); i++) {
      statuseCodes[i] = ecodes.get(i).geteCode();
    }

    condition.setStatuseCodes(statuseCodes);
    Set<Map<String, Object>> set = productionOrderDao.getProductionOrdersCountByStatus(condition);
    for (Map<String, Object> map : set) {
      Integer count = Integer.valueOf(map.get("COUNT").toString());
      orderStatusMap.put(map.get("STATUSECODE").toString(), count);
      totalCount += count;
    }

    condition
        .setStatuseCodes(new String[] { OrderStatusCode.CREATE.getValue(), OrderStatusCode.PLACE_ORDER.getValue() });
    orderStatusMap.put("uncompleteCount", productionOrderDao.getUncompletedCount(condition));
    orderStatusMap.put("totalCount", totalCount);
    condition.setStatuseCodes(new String[] {});
    return orderStatusMap;
  }

  @Override
  public void closeProductionOrder(ProductionOrder order) {
    if (order == null) {
      return;
    }
    if (StringUtils.isBlank(order.getSiteCode()) || StringUtils.isBlank(order.getProductionOrderNum())) {
      return;
    }

    productionControlService.closeProductionControl(order.getSiteCode(), order.getProductionOrderNum(),
        "productionOrder");

    order.setModifyDateTime(new Date());
    order.setModifyUser(SecurityContextUtil.getCurrentUserName());
    order.setStatuseCode(OrderStatusCode.CLOSE.getValue());
    productionOrderDao.closeProductionOrder(order);

    if (StringUtils.isBlank(order.getProductOrderNum())) {
      return;
    }
    //如果子订单全部关闭则关闭产品订单
    List<ProductionOrder> orders = productionOrderDao.selectUnClosedProductionOrdersByProductOrderNum(
        order.getSiteCode(), order.getProductOrderNum(), OrderStatusCode.CLOSE.getValue());
    if (orders == null || orders.size() == 0) {
      ProductOrder productOrder = new ProductOrder();
      productOrder.setSiteCode(order.getSiteCode());
      productOrder.setProductOrderNum(order.getProductOrderNum());
      productOrder.setStatuseCode(PorderStatusCode.CLOSE.getValue());
      productOrder.setModifyDateTime(new Date());
      productOrder.setModifyUser(SecurityContextUtil.getCurrentUserName());
      productOrderDao.updateNoCreateProductOrder(productOrder);
    }

  }

  @Override
  public void doCloseProductionOrder(String siteCode, String productionOrderNum) {
    if (StringUtils.isBlank(siteCode) || StringUtils.isBlank(productionOrderNum)) {
      return;
    }
    ProductionOrder order = this.getProductionOrderByPk(siteCode, productionOrderNum);
    String statusCode = order.getStatuseCode();
    ECode ecode = eCodeService.selectEcodeByECodeTypeAndLanguageCodeAndECode(order.getStatuseCode(),
        ECodeEnums.PRODUCTIONORDER_TYPE.getCode(), SecurityContextUtil.getLanguageCode());
    if (OrderStatusCode.COMPLETE.getValue().equals(statusCode) || OrderStatusCode.CLOSE.getValue().equals(statusCode)) {
      throw ImeException.newException("productionOrder_notClose.M", new String[] { ecode.geteCodeText() });
    }

    this.closeProductionOrder(order);
  }

  @Override
  public void doSplitProduction(ProductionOrder order) {

    ProductionOrder oldOrder = this.getProductionOrderByPk(order.getSiteCode(), order.getProductionOrderNum());
    //保存数据之前校验数据状态
    /*  if (null != oldOrder && !OrderStatusCode.CREATE.getValue().equals(oldOrder.getStatuseCode())) {
        ECode ecode = eCodeService.selectEcodeByECodeTypeAndLanguageCodeAndECode(oldOrder.getStatuseCode(),
            ECodeEnums.PRODUCTIONORDER_TYPE.getCode(), SecurityContextUtil.getLanguageCode());
        throw ImeException.newException("productionOrder_notIssued.M", new String[] { ecode.geteCodeText() });
      }*/
    //校验数量
    double count = 0;
    for (ProductionControl contorl : order.getControls()) {
      if (contorl != null && null != contorl.getPlannedQuantity()) {
        count += contorl.getPlannedQuantity();
      }
    }
    double unReleasedQuantity = oldOrder.getPlannedQuantity().doubleValue()
        - oldOrder.getReleasedQuantity().doubleValue();
    if (count > unReleasedQuantity) {
      throw ImeException.newException("order_planQuantity_neq.M", new String[] { count + "", unReleasedQuantity + "" });
    }
    //定义流程变量
    oldOrder.setStatuseCode(OrderStatusCode.PLACE_ORDER.getValue());
    //下达数量
    oldOrder.setReleasedQuantity(oldOrder.getReleasedQuantity() + count);
    oldOrder.setControls(order.getControls());
    Map<String, Object> map = new HashMap<String, Object>();
    //变量生产订单
    map.put("productionOrder", JSONObject.toJSON(oldOrder));
    try {
      skyProcessEngine.startProcessByKeyAndTenantId(map, "issuedProductionOrder_process", order.getSiteCode());
    } catch (Exception e) {
      throw ImeException.newException(SystemConstant.DWZ_CODE_ERROR_MESSAGE, e);
    }
  }

  @Override
  public List<OperationArchives> drawProcessOperation(String siteCode, String processCode, String processRev) {
    if (StringUtils.isBlank(processCode) || StringUtils.isBlank(processRev) || StringUtils.isBlank(siteCode)) {
      return Collections.emptyList();
    }
    List<ProcessOperation> pops = processService.getProcessOperationByProcessCode(siteCode, processCode, processRev);
    if (pops == null || pops.isEmpty()) {
      return Collections.emptyList();
    }
    Map<Integer, List<ProcessOperation>> map = new HashMap<Integer, List<ProcessOperation>>();
    int count = pops.size() / 6;
    count = pops.size() % 6 == 0 ? count : count + 1;
    for (int i = 1; i <= count; i++) {
      int end = i * 6;
      end = end > pops.size() ? pops.size() : end;
      map.put(i, pops.subList((i - 1) * 6, end));
    }

    List<OperationArchives> oas = new ArrayList<OperationArchives>();
    Set<Entry<Integer, List<ProcessOperation>>> set = map.entrySet();
    for (Entry<Integer, List<ProcessOperation>> entry : set) {
      OperationArchives oa = new OperationArchives(entry.getKey() % 2 == 0 ? false : true, entry.getValue().size(),
          entry.getValue());
      oas.add(oa);
    }
    return oas;
  }

  @Override
  public ProductionOrderAttachment saveProductionOrderAttachment(String siteCode, MultipartFile file) {
    if (!StringUtils.isBlank(siteCode)) {
      String filePath = SkyFileUtils.buildFilePath("productionOrder");
      String fileName = file.getOriginalFilename();
      String md5 = "";
      try {
        md5 = SkyFileUtils.getMD5String(file.getBytes());
      } catch (IOException e) {
        LOGGER.error("{}", e);
      }
      ProductionOrderAttachment attachMent = new ProductionOrderAttachment();
      attachMent.setSiteCode(siteCode);
      attachMent.setFileName(fileName);
      attachmentService.checkIsExtisAttachment(attachMent, md5);
      //MD5校验，映射文件
      if (StringUtils.isBlank(attachMent.getAttachmentCode())) {
        String saveName = UUID.randomUUID().toString();
        String newFileName = SkyFileUtils.renameFileName(fileName, saveName);
        FTPUtils ftpUtils = (FTPUtils) SpringBeanUtil.getBean("ftpUtils");
        String savePath = ftpUtils.uploadMultipartFiles(file, filePath, newFileName);
        Long size = file.getSize();
        attachMent.setAttachmentCode(saveName);
        attachMent.setFileSize(size);
        attachMent.setSavePath(savePath);
        Attachment att = new Attachment(saveName, savePath, fileName, size, md5, 1);
        att.setSiteCode(siteCode);
        attachmentService.insertAttachment(att);
      }
      return attachMent;
    }
    return null;
  }

  @Override
  public List<ProductionOrderAttachment> getProductionOrderAttachment(String siteCode, String productionOrderNum) {
    return productionOrderAttachmentDao.selectAttachmentsByProductionNum(siteCode, productionOrderNum);
  }

}
